<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha256-Z1K5uhUaJXA7Ll0XrZ/0JhX4lAtZFpT6jkKrEDT0drU=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"example.com","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.14.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":-1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta name="description" content="GC的基础知识一、垃圾回收的区域 栈：栈中的生命周期是跟随线程，所以一般不需要关注。 堆：堆中的对象是垃圾回收的重点。 方法区：这一块也会发生垃圾回收，不过这块的效率比较低，一般不是我们关注的重点。  二、怎么判断对象的存活一般有两种方式（引用计数法、可达性分析），JVM使用的是可达性分析。 1. 引用计数法给对象添加一个引用计数器，当对象增加一个引用时计数器加 1，引用失效时计数器减 1。引用计">
<meta property="og:type" content="article">
<meta property="og:title" content="JVM垃圾回收算法和垃圾回收器">
<meta property="og:url" content="http://example.com/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/index.html">
<meta property="og:site_name" content="JsyBlog">
<meta property="og:description" content="GC的基础知识一、垃圾回收的区域 栈：栈中的生命周期是跟随线程，所以一般不需要关注。 堆：堆中的对象是垃圾回收的重点。 方法区：这一块也会发生垃圾回收，不过这块的效率比较低，一般不是我们关注的重点。  二、怎么判断对象的存活一般有两种方式（引用计数法、可达性分析），JVM使用的是可达性分析。 1. 引用计数法给对象添加一个引用计数器，当对象增加一个引用时计数器加 1，引用失效时计数器减 1。引用计">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://example.com/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/mark-sweep.png">
<meta property="og:image" content="http://example.com/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-copy.png">
<meta property="og:image" content="http://example.com/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/mark-compact.png">
<meta property="og:image" content="http://example.com/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-generation.png">
<meta property="og:image" content="http://example.com/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-machines.png">
<meta property="og:image" content="http://example.com/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-cms.png">
<meta property="og:image" content="http://example.com/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/g1-region.png">
<meta property="og:image" content="http://example.com/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-g1.png">
<meta property="article:published_time" content="2021-12-12T10:31:40.000Z">
<meta property="article:modified_time" content="2023-01-04T19:04:06.910Z">
<meta property="article:author" content="SongyangJi">
<meta property="article:tag" content="JVM">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://example.com/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/mark-sweep.png">


<link rel="canonical" href="http://example.com/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":true,"permalink":"http://example.com/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/","path":"2021/12/12/JVM垃圾回收算法和垃圾回收器/","title":"JVM垃圾回收算法和垃圾回收器"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>JVM垃圾回收算法和垃圾回收器 | JsyBlog</title>
  








  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">JsyBlog</p>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#GC%E7%9A%84%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86"><span class="nav-number">1.</span> <span class="nav-text">GC的基础知识</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%B8%80%E3%80%81%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%9A%84%E5%8C%BA%E5%9F%9F"><span class="nav-number">1.1.</span> <span class="nav-text">一、垃圾回收的区域</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BA%8C%E3%80%81%E6%80%8E%E4%B9%88%E5%88%A4%E6%96%AD%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%AD%98%E6%B4%BB"><span class="nav-number">1.2.</span> <span class="nav-text">二、怎么判断对象的存活</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0%E6%B3%95"><span class="nav-number">1.2.1.</span> <span class="nav-text">1. 引用计数法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-%E5%8F%AF%E8%BE%BE%E6%80%A7%E5%88%86%E6%9E%90"><span class="nav-number">1.2.2.</span> <span class="nav-text">2. 可达性分析</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%B8%89%E3%80%81-%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95"><span class="nav-number">1.3.</span> <span class="nav-text">三、 垃圾回收算法</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-%E6%A0%87%E8%AE%B0-%E6%B8%85%E9%99%A4%E7%AE%97%E6%B3%95"><span class="nav-number">1.3.1.</span> <span class="nav-text">1. 标记-清除算法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-%E6%A0%87%E8%AE%B0-%E5%A4%8D%E5%88%B6%E7%AE%97%E6%B3%95"><span class="nav-number">1.3.2.</span> <span class="nav-text">2. 标记-复制算法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-%E6%A0%87%E8%AE%B0-%E6%95%B4%E7%90%86%E7%AE%97%E6%B3%95"><span class="nav-number">1.3.3.</span> <span class="nav-text">3. 标记-整理算法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-%E5%88%86%E4%BB%A3%E6%94%B6%E9%9B%86%E6%8A%80%E6%9C%AF"><span class="nav-number">1.3.4.</span> <span class="nav-text">4. 分代收集技术</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%9F%BA%E6%9C%AC%E6%9C%AF%E8%AF%AD"><span class="nav-number">1.3.4.1.</span> <span class="nav-text">基本术语</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E6%96%B0%E7%94%9F%E4%BB%A3%EF%BC%88Young-generation%EF%BC%89"><span class="nav-number">1.3.4.2.</span> <span class="nav-text">新生代（Young generation）</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E8%80%81%E5%B9%B4%E4%BB%A3%EF%BC%88Old-generation%EF%BC%89"><span class="nav-number">1.3.4.3.</span> <span class="nav-text">老年代（Old generation）</span></a></li></ol></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8"><span class="nav-number">2.</span> <span class="nav-text">垃圾回收器</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%96%B0%E7%94%9F%E4%BB%A3%EF%BC%9A%E5%A4%8D%E5%88%B6%E7%AE%97%E6%B3%95%E3%80%82"><span class="nav-number">2.1.</span> <span class="nav-text">新生代：复制算法。</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%80%81%E5%B9%B4%E4%BB%A3%EF%BC%9A%E6%A0%87%E8%AE%B0%E6%B8%85%E9%99%A4%E7%AE%97%E6%B3%95%E5%92%8C%E6%A0%87%E8%AE%B0%E6%95%B4%E7%90%86%E7%AE%97%E6%B3%95"><span class="nav-number">2.2.</span> <span class="nav-text">老年代：标记清除算法和标记整理算法</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Serial-x2F-Serial-Old"><span class="nav-number">2.3.</span> <span class="nav-text">Serial&#x2F;Serial Old</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ParNew"><span class="nav-number">2.4.</span> <span class="nav-text">ParNew</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Parallel-Scavenge%EF%BC%88ParallerGC%EF%BC%89-x2F-Parallel-Old"><span class="nav-number">2.5.</span> <span class="nav-text">Parallel Scavenge（ParallerGC）&#x2F;Parallel Old</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#CMS%EF%BC%88Concurrent-Mark-Sweep%EF%BC%89"><span class="nav-number">2.6.</span> <span class="nav-text">CMS（Concurrent Mark Sweep）</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%AD%A5%E9%AA%A4"><span class="nav-number">2.6.1.</span> <span class="nav-text">步骤</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BC%98%E7%BC%BA%E7%82%B9"><span class="nav-number">2.6.2.</span> <span class="nav-text">优缺点</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#G1"><span class="nav-number">2.7.</span> <span class="nav-text">G1</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Region%E5%A6%82%E4%BD%95%E5%88%92%E5%88%86"><span class="nav-number">2.7.1.</span> <span class="nav-text">Region如何划分</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%AD%A5%E9%AA%A4-1"><span class="nav-number">2.7.2.</span> <span class="nav-text">步骤</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8%E7%9A%84%E4%B8%80%E4%BA%9B%E9%87%8D%E8%A6%81%E5%8F%82%E6%95%B0"><span class="nav-number">2.8.</span> <span class="nav-text">垃圾回收器的一些重要参数</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#FullGC"><span class="nav-number">3.</span> <span class="nav-text">FullGC</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E4%BC%9A%E8%A7%A6%E5%8F%91FullGC"><span class="nav-number">3.1.</span> <span class="nav-text">什么时候会触发FullGC</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#System-gc-%E6%96%B9%E6%B3%95%E7%9A%84%E8%B0%83%E7%94%A8"><span class="nav-number">3.1.1.</span> <span class="nav-text">System.gc()方法的调用</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Metaspace%E5%8C%BA%E5%86%85%E5%AD%98%E8%BE%BE%E5%88%B0%E9%98%88%E5%80%BC"><span class="nav-number">3.1.2.</span> <span class="nav-text">Metaspace区内存达到阈值</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%80%81%E5%B9%B4%E4%BB%A3%E7%A9%BA%E9%97%B4%E4%B8%8D%E8%B6%B3"><span class="nav-number">3.1.3.</span> <span class="nav-text">老年代空间不足</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%A0%86%E4%B8%AD%E5%88%86%E9%85%8D%E5%BE%88%E5%A4%A7%E7%9A%84%E5%AF%B9%E8%B1%A1"><span class="nav-number">3.1.4.</span> <span class="nav-text">堆中分配很大的对象</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BB%9F%E8%AE%A1%E5%BE%97%E5%88%B0%E7%9A%84Minor-GC%E6%99%8B%E5%8D%87%E5%88%B0%E6%97%A7%E7%94%9F%E4%BB%A3%E7%9A%84%E5%B9%B3%E5%9D%87%E5%A4%A7%E5%B0%8F%E5%A4%A7%E4%BA%8E%E8%80%81%E5%B9%B4%E4%BB%A3%E7%9A%84%E5%89%A9%E4%BD%99%E7%A9%BA%E9%97%B4"><span class="nav-number">3.1.5.</span> <span class="nav-text">统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#CMS-GC%E6%97%B6%E5%87%BA%E7%8E%B0promotion-failed%E5%92%8Cconcurrent-mode-failure"><span class="nav-number">3.1.6.</span> <span class="nav-text">CMS GC时出现promotion failed和concurrent mode failure</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%80%BB%E7%BB%93"><span class="nav-number">3.1.7.</span> <span class="nav-text">总结</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%A6%82%E4%BD%95%E6%8E%92%E6%9F%A5"><span class="nav-number">3.2.</span> <span class="nav-text">如何排查</span></a></li></ol></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">SongyangJi</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">251</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">45</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">109</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="JVM垃圾回收算法和垃圾回收器 | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          JVM垃圾回收算法和垃圾回收器
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-12 18:31:40" itemprop="dateCreated datePublished" datetime="2021-12-12T18:31:40+08:00">2021-12-12</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-01-05 03:04:06" itemprop="dateModified" datetime="2023-01-05T03:04:06+08:00">2023-01-05</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JVM/" itemprop="url" rel="index"><span itemprop="name">JVM</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <h1 id="GC的基础知识"><a href="#GC的基础知识" class="headerlink" title="GC的基础知识"></a>GC的基础知识</h1><h2 id="一、垃圾回收的区域"><a href="#一、垃圾回收的区域" class="headerlink" title="一、垃圾回收的区域"></a>一、垃圾回收的区域</h2><ul>
<li>栈：栈中的生命周期是跟随线程，所以一般不需要关注。</li>
<li>堆：堆中的对象是垃圾回收的重点。</li>
<li>方法区：这一块也会发生垃圾回收，不过这块的效率比较低，一般不是我们关注的重点。</li>
</ul>
<h2 id="二、怎么判断对象的存活"><a href="#二、怎么判断对象的存活" class="headerlink" title="二、怎么判断对象的存活"></a>二、怎么判断对象的存活</h2><p>一般有两种方式（引用计数法、可达性分析），<strong>JVM使用的是可达性分析</strong>。</p>
<h3 id="1-引用计数法"><a href="#1-引用计数法" class="headerlink" title="1. 引用计数法"></a>1. 引用计数法</h3><p>给对象添加一个引用计数器，当对象增加一个引用时计数器加 1，引用失效时计数器减 1。引用计数为 0 的对象可被回收（Python 在用，但主流虚拟机没有使用）。</p>
<ul>
<li>优点：快，方便，实现简单。</li>
<li>缺陷：对象相互引用时（A.instance&#x3D;B 同时 B.instance&#x3D;A），很难判断对象是否该回收。</li>
</ul>
<h3 id="2-可达性分析"><a href="#2-可达性分析" class="headerlink" title="2. 可达性分析"></a>2. 可达性分析</h3><p>来判定对象是否存活的。这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点，从这些节点开始向下搜索，搜索所走过的路径称为引用链（Reference Chain），当一个对象到 GC Roots 没有任何引用链相连时，则证明此对象是不可用的。</p>
<p>作为 GC Roots 的对象包括下面几种：</p>
<ul>
<li>虚拟机栈（栈帧中的本地变量表）中引用的对象；</li>
<li>方法区中类静态属性引用的对象；</li>
<li>方法区中常量引用的对象；</li>
<li>本地方法栈中JNI（即一般说的Native方法）引用的对象；</li>
</ul>
<h2 id="三、-垃圾回收算法"><a href="#三、-垃圾回收算法" class="headerlink" title="三、 垃圾回收算法"></a>三、 垃圾回收算法</h2><p>本节具体介绍一下各种垃圾回收算法的思想：</p>
<h3 id="1-标记-清除算法"><a href="#1-标记-清除算法" class="headerlink" title="1. 标记-清除算法"></a>1. 标记-清除算法</h3><p>标记-清除算法对<strong>根集合</strong>进行扫描，对<strong>存活</strong>的对象进行标记。标记完成后，再对整个空间内<strong>未被标记</strong>的对象扫描，进行回收。</p>
<ul>
<li><p><strong>优点</strong>：</p>
<p><strong>实现简单，不需要进行对象进行移动</strong>。</p>
</li>
<li><p><strong>缺点</strong>：</p>
<p>标记、清除过程效率低，<strong>产生大量不连续的内存碎片</strong>，提高了垃圾回收的频率。</p>
</li>
</ul>
<p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/mark-sweep.png"></p>
<h3 id="2-标记-复制算法"><a href="#2-标记-复制算法" class="headerlink" title="2. 标记-复制算法"></a>2. 标记-复制算法</h3><p>这种收集算法解决了标记清除算法存在的效率问题。它将内存区域划分成相同的两个<strong>内存块</strong>。每次仅使用一半的空间，<code>JVM</code>生成的新对象放在一半空间中。当一半空间用完时进行<code>GC</code>，把可到达对象复制到另一半空间，然后把使用过的内存空间一次清理掉。</p>
<ul>
<li><p><strong>优点</strong>：</p>
<p>按顺序分配内存即可，实现简单、运行高效，不用考虑内存碎片。</p>
</li>
<li><p><strong>缺点</strong>：</p>
<p><strong>可用的内存大小缩小为原来的一半</strong>，<strong>对象存活率高时会频繁进行复制</strong>。</p>
</li>
</ul>
<p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-copy.png"></p>
<h3 id="3-标记-整理算法"><a href="#3-标记-整理算法" class="headerlink" title="3. 标记-整理算法"></a>3. 标记-整理算法</h3><p><strong>标记-整理算法</strong> 采用和 <strong>标记-清除算法</strong> 一样的方式进行对象的标记，但后续不直接对可回收对象进行清理，而是将所有的<strong>存活对象</strong>往一端<strong>空闲空间</strong>移动，然后清理掉端边界以外的内存空间。</p>
<ul>
<li><p><strong>优点</strong>：</p>
<p><strong>解决了标记-清理算法存在的内存碎片问题</strong>。</p>
</li>
<li><p><strong>缺点</strong>：</p>
<p>仍<strong>需要进行局部对象移动，一定程度上降低了效率</strong>。</p>
<p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/mark-compact.png"></p>
</li>
</ul>
<h3 id="4-分代收集技术"><a href="#4-分代收集技术" class="headerlink" title="4. 分代收集技术"></a>4. 分代收集技术</h3><h4 id="基本术语"><a href="#基本术语" class="headerlink" title="基本术语"></a>基本术语</h4><ul>
<li>部分收集（Partial GC）指目标不是完整收集整个Java堆的垃圾收集，其中可以分为：</li>
<li><ul>
<li>新生代收集（Minor GC&#x2F;Young GC）: 指目标只是新生代的垃圾收集；</li>
<li>老年代收集（Major GC&#x2F;Old GC）: 指目标只是老年代的垃圾收集。注意这个名词可能有歧义，有的也指整堆收集。</li>
<li>混合收集（Mixed GC）：指目标是收集整个新生代以及部分老年代的来及收集。目前只有G1收集器有这种行为。</li>
</ul>
</li>
<li>整堆收集（Full GC）：收集整个Java堆和方法区的垃圾收集。</li>
</ul>
<p>当前商业虚拟机都采用<strong>分代收集</strong>的垃圾收集算法。分代收集算法，顾名思义是根据对象的<strong>存活周期</strong>将内存划分为几块。一般包括<strong>年轻代</strong>、<strong>老年代</strong> 和 <strong>永久代</strong>，如图所示：</p>
<p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-generation.png"></p>
<blockquote>
<p>上图中的 永久（Permanent）代在 JDK8之后便被取消。</p>
</blockquote>
<h4 id="新生代（Young-generation）"><a href="#新生代（Young-generation）" class="headerlink" title="新生代（Young generation）"></a>新生代（Young generation）</h4><p>绝大多数最新被创建的对象会被分配到这里，由于<strong>大部分对象</strong>在创建后会很快变得<strong>不可达</strong>，所以很多对象被创建在<strong>新生代</strong>，然后<strong>消失</strong>。对象从这个区域消失的过程我们称之为 <code>minor GC</code>。</p>
<p><strong>新生代</strong> 中存在一个<code>Eden</code>区和两个<code>Survivor</code>区（Survivor 区里面分为 from 和 to 区），每次只使用Eden和其中的一块Survivor区。新对象会首先分配在<code>Eden</code>中（<strong>如果新对象过大，会直接分配在老年代</strong>中）。在<code>GC</code>中，<code>Eden</code>中的对象会被移动到<code>Survivor</code>中，直至对象满足一定的年纪（定义为熬过<code>GC</code>的次数），会被移动到<strong>老年代</strong>。</p>
<p>可以设置<strong>新生代</strong>和<strong>老年代</strong>的相对大小。这种方式的优点是新生代大小会随着整个<strong>堆</strong>大小<strong>动态扩展</strong>。参数 <code>-XX:NewRatio</code> 设置<strong>老年代</strong>与<strong>新生代</strong>的比例。例如 <code>-XX:NewRatio=8</code> 指定 <strong>老年代&#x2F;新生代</strong> 为<code>8/1</code>. <strong>老年代</strong> 占堆大小的 <code>7/8</code> ，<strong>新生代</strong> 占堆大小的 <code>1/8</code>（默认即是 <code>1/8</code>）。</p>
<p><strong>当Eden区满了的时候，会触发Young GC。</strong></p>
<h4 id="老年代（Old-generation）"><a href="#老年代（Old-generation）" class="headerlink" title="老年代（Old generation）"></a>老年代（Old generation）</h4><p>对象没有变得不可达，并且从新生代中<strong>存活</strong>下来，会被<strong>拷贝</strong>到这里。其所占用的空间要比新生代多。也正由于其相对<strong>较大的空间</strong>，发生在<strong>老年代</strong>上的<code>GC</code>要比<strong>新生代</strong>要<strong>少得多</strong>。对象从<strong>老年代</strong>中消失的过程，可以称之为<code>major GC</code>（或者<code>full GC</code>）。</p>
<p><code>JDK8</code>堆内存一般是划分为<strong>年轻代</strong>和<strong>老年代</strong>，<strong>不同年代</strong> 根据自身特性采用<strong>不同的垃圾收集算法</strong>。</p>
<p>对于<strong>新生代</strong>，每次<code>GC</code>时都有<strong>大量</strong>的对象死亡，只有<strong>少量</strong>对象存活。考虑到复制成本低，适合采用<strong>复制算法</strong>。因此有了<code>From Survivor</code>和<code>To Survivor</code>区域。</p>
<p>对于<strong>老年代</strong>，因为对象<strong>存活率高</strong>，没有额外的内存空间对它进行担保。因而适合采用<strong>标记-清除算法</strong>和<strong>标记-整理算法</strong>进行回收。</p>
<h1 id="垃圾回收器"><a href="#垃圾回收器" class="headerlink" title="垃圾回收器"></a>垃圾回收器</h1><p>Jvm 垃圾回收器把上面的三种算法全部用到了，采用分代收集。</p>
<h2 id="新生代：复制算法。"><a href="#新生代：复制算法。" class="headerlink" title="新生代：复制算法。"></a>新生代：复制算法。</h2><table>
<thead>
<tr>
<th>收集器</th>
<th>收集对象和算法</th>
<th>收集器类型</th>
</tr>
</thead>
<tbody><tr>
<td>Serial</td>
<td>新生代，<strong>标记复制算法</strong></td>
<td>单线程</td>
</tr>
<tr>
<td>ParNew</td>
<td>新生代，<strong>标记复制算法</strong></td>
<td>并行的多线程收集器</td>
</tr>
<tr>
<td>Parallel Scavenge</td>
<td>新生代，<strong>标记复制算法</strong></td>
<td>并行的多线程收集器</td>
</tr>
</tbody></table>
<h2 id="老年代：标记清除算法和标记整理算法"><a href="#老年代：标记清除算法和标记整理算法" class="headerlink" title="老年代：标记清除算法和标记整理算法"></a>老年代：标记清除算法和标记整理算法</h2><table>
<thead>
<tr>
<th>收集器</th>
<th>收集对象和算法</th>
<th>收集器类型</th>
</tr>
</thead>
<tbody><tr>
<td>Serial Old</td>
<td>老年代，标记整理算法</td>
<td>单线程</td>
</tr>
<tr>
<td>Parallel Old</td>
<td>老年代，标记整理算法</td>
<td>并行的多线程收集器</td>
</tr>
<tr>
<td>CMS（Concurrent Mark Sweep ）</td>
<td>老年代，<strong>标记清除算法</strong></td>
<td>并行和并发收集器</td>
</tr>
<tr>
<td>G1（Garbage First）</td>
<td>跨新生代和老年代，复制算法 + 标记整理算法</td>
<td>并行和并发收集器</td>
</tr>
</tbody></table>
<p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-machines.png"></p>
<p><strong>注：</strong></p>
<ul>
<li><strong>并行：垃圾收集的多线程的同时进行</strong>。</li>
<li><strong>并发：垃圾收集的多线程和用户应用的多线程同时进行</strong>。</li>
<li>使用 <code>jps -v</code> 可以看到使用的垃圾收集器，例如：<code>-XX:+UseConcMarkSweepGC</code> （CMS）</li>
</ul>
<h2 id="Serial-x2F-Serial-Old"><a href="#Serial-x2F-Serial-Old" class="headerlink" title="Serial&#x2F;Serial Old"></a>Serial&#x2F;Serial Old</h2><p>最古老的，单线程，独占式，成熟，适合单 CPU 服务器。<code>-XX:+UseSerialGC</code> 新生代和老年代都用串行收集器。</p>
<h2 id="ParNew"><a href="#ParNew" class="headerlink" title="ParNew"></a>ParNew</h2><p>ParNew 和 Serial 基本没区别，唯一的区别：多线程，多 CPU 的，停顿时间比 Serial 少 。</p>
<p><code>-XX:+UseParNewGC</code>新生代使用 ParNew，老年代使用 Serial Old 。</p>
<p>可以和CMS搭配使用。</p>
<h2 id="Parallel-Scavenge（ParallerGC）-x2F-Parallel-Old"><a href="#Parallel-Scavenge（ParallerGC）-x2F-Parallel-Old" class="headerlink" title="Parallel Scavenge（ParallerGC）&#x2F;Parallel Old"></a>Parallel Scavenge（ParallerGC）&#x2F;Parallel Old</h2><p><strong>关注吞吐量的垃圾收集器</strong>，<strong>高吞吐量则可以高效率地利用 CPU 时间</strong>（但对响应时间有负面影响），尽快完成程序的运算任务，主要适合在后台运算而不需要太多交互的任务。所谓吞吐量就是 CPU 用于运行用户代码的时间与 CPU 总消耗时间的比值，即吞吐量&#x3D;运行用户代码时间&#x2F;（运行用户代码时间+垃圾收集时间）</p>
<h2 id="CMS（Concurrent-Mark-Sweep）"><a href="#CMS（Concurrent-Mark-Sweep）" class="headerlink" title="CMS（Concurrent Mark Sweep）"></a>CMS（Concurrent Mark Sweep）</h2><p>CMS收集器是一种<strong>以获取最短回收停顿时间为目标</strong>的收集器。目前很大一部分的 Java 应用集中在互联网站或者 B&#x2F;S 系统的服务端上，这类应用尤其<strong>重视服务的响应速度，希望系统停顿时间最短</strong>，以给用户带来较好的体验。</p>
<p>CMS 收集器就非常符合这类应用的需求。<code>-XX:+UseConcMarkSweepGC</code> ，<strong>一般新生代使用 ParNew，老年代的用 CMS</strong>，从名字（包含“Mark Sweep”）上就可以看出，CMS 收集器是基于“标记—清除”算法实现的，它的运作过程相对于前面几种收集器来说更复杂一些。</p>
<h3 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h3><p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-cms.png"></p>
<p>整个过程分为 4 个步骤，包括：</p>
<p>1、<strong>初始标记</strong>：仅仅只是标记一下 <strong>GC Roots 能直接关联到的对象</strong>，速度很快，需要停顿（STW -Stop the world）。（需要 STW）</p>
<p>2、<strong>并发标记</strong>：从 GC Root 开始对堆中对象进行可达性分析，找到存活对象，它在整个回收过程中耗时最长，不需要停顿。（无需STW）</p>
<p>3、<strong>重新标记</strong>：<strong>为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录，</strong>。这个阶段的停顿时间一般会比初始标记阶段稍长一些，但远比并发标记的时间短。(因为用户在并发标记阶段对对象做的修改相对来讲是比较少的)。（需要STW)</p>
<ol start="4">
<li><strong>并发清除</strong>：（无需STW）</li>
</ol>
<blockquote>
<p><strong>Stop The World</strong></p>
<p>Java中Stop-The-World机制简称STW，是在执行垃圾收集算法时，Java应用程序的其他所有线程都被挂起（除了垃圾收集帮助器之外）。Java中一种全局暂停现象，全局停顿，所有Java代码停止，native代码可以执行，但不能与JVM交互。</p>
</blockquote>
<h3 id="优缺点"><a href="#优缺点" class="headerlink" title="优缺点"></a>优缺点</h3><ol>
<li>优点</li>
</ol>
<p>由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作，所以，从<strong>总体上来说，CMS 收集器的内存回收过程是与用户线程一起并发执行的</strong>。</p>
<ol start="2">
<li>缺点</li>
</ol>
<ul>
<li><strong>CPU 资源敏感</strong>：因为并发阶段多线程占据 CPU 资源，如果 CPU 资源不足，效率会明显降低。</li>
<li><strong>会产生内存碎片</strong>：<strong>标记-清除算法</strong> 会导致产生不连续的内存碎片。</li>
<li>由于 CMS <strong>并发清理阶段</strong> 用户线程还在运行着，伴随程序运行自然就还会有新的垃圾不断产生，这一部分垃圾出现在标记过程之后，CMS 无法在当次收集中处理掉它们，只好留待下一次 GC 时再清理掉。这一部分垃圾就称为 <strong>浮动垃圾</strong>。</li>
<li>由于浮动垃圾的存在，因此需要预留出一部分内存，意味着 CMS 收集不能像其它收集器那样等待老年代快满的时候再回收。在 1.6 的版本中老年代空间使用率阈值(92%)，如果预留的内存不够存放浮动垃圾，就会出现 Concurrent Mode Failure，这时虚拟机将临时启用 Serial Old 来替代 CMS。</li>
</ul>
<h2 id="G1"><a href="#G1" class="headerlink" title="G1"></a>G1</h2><p><strong>G1相比较CMS的改进</strong></p>
<ul>
<li><strong>基于标记-整理算法, 不会产生空间碎片</strong>，分配大对象时不会无法得到连续的空间而提前触发一次full gc 。</li>
<li><strong>停顿时间相对可控</strong>： G1可以通过设置预期停顿时间（Pause time）来控制垃圾收集时间，但是<strong>这个预期停顿时间G1只能尽量做到，而不是一定能做到</strong>。</li>
</ul>
<p>G1 收集器之所以能建立可预测的停顿时间模型，是因为它可以<strong>有计划地避免在整个 Java 堆中进行全区域的垃圾收集</strong>。</p>
<p>G1 跟踪各个 Region 里面的垃圾堆积的价值大小（回收所获得的空间大小以及回收所需时间的经验值），<strong>在后台维护一个优先列表</strong>，<strong>每次根据允许的收集时间，优先回收价值最大的 Region</strong>（这也就是 <strong>Garbage-First</strong> 名称的来由）。</p>
<p><strong>这种使用 Region 划分内存空间以及有优先级的区域回收方式</strong>，<strong>保证了 G1 收集器在有限的时间内可以获取尽可高的收集效率</strong>。</p>
<h3 id="Region如何划分"><a href="#Region如何划分" class="headerlink" title="Region如何划分"></a>Region如何划分</h3><p>G1 把堆划分成多个大小相等的 <strong>独立区域</strong>（Region），<strong>新生代和老年代不再物理隔离</strong>。</p>
<p>G1 算法将堆划分为若干个独立区域（Region），它仍然属于分代收集器。</p>
<p>不过，这些区域的一部分包含新生代，<strong>新生代的垃圾收集依然采用暂停所有应用线程的方式，将存活对象拷贝到老年代或者 Survivor 空间</strong>。</p>
<p>例如其中一个独立区域如图：</p>
<p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/g1-region.png"></p>
<h3 id="步骤-1"><a href="#步骤-1" class="headerlink" title="步骤"></a>步骤</h3><p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-g1.png"></p>
<p>大致分为4个步骤：</p>
<ol>
<li><strong>初始标记</strong>：仅仅只是标记一下 GC Roots 能直接关联到的对象，并且修改 TAMS（Nest Top Mark Start）的值，让下一阶段用户程序并发运行时，能在正确可以的 Region 中创建对象，此阶段需要停顿线程(STW)，但耗时很短。</li>
<li><strong>并发标记</strong>：从 GC Root 开始对堆中对象进行可达性分析，找到存活对象，此阶段耗时较长，但可与用户程序并发执行。</li>
<li><strong>最终标记</strong>：<strong>为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录</strong>，虚拟机将这段时间对象变化记录在线程的 <code>Remembered Set Logs</code> 里面，最终标记阶段需要把 Remembered Set Logs 的数据合并到 Remembered Set 中。这阶段需要停顿线程(STW)，但是可并行执行。</li>
<li><strong>筛选回收</strong>：<strong>首先对各个 Region 中的回收价值和成本进行排序，根据用户所期望的 GC 停顿时间来制定回收计划</strong>。此阶段其实也可以做到与用户程序一起并发执行，但是因为只回收一部分 Region，时间是用户可控制的，而且停顿用户线程将大幅度提高收集效率。</li>
</ol>
<p>G1 从整体来看是基于<strong>标记整理</strong>算法实现的收集器，从局部（两个Region之间）上来看是基于复制算法实现的。</p>
<h2 id="垃圾回收器的一些重要参数"><a href="#垃圾回收器的一些重要参数" class="headerlink" title="垃圾回收器的一些重要参数"></a>垃圾回收器的一些重要参数</h2><p>使用<code>java -XX:+PrintCommandLineFlags -version</code>查看关于垃圾收集器的一些信息。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC</span><br><span class="line">java version &quot;1.8.0_281&quot;</span><br><span class="line">Java(TM) SE Runtime Environment (build 1.8.0_281-b09)</span><br><span class="line">Java HotSpot(TM) 64-Bit Server VM (build 25.281-b09, mixed mode)</span><br></pre></td></tr></table></figure>



<table>
<thead>
<tr>
<th>参数</th>
<th>描述</th>
</tr>
</thead>
<tbody><tr>
<td>UseSerialGC</td>
<td>虚拟机运行在 Client 模式下的默认值，打开此开关后，使用 Serial+Serial Old 的收集器组合进行内存回收</td>
</tr>
<tr>
<td>UseParNewGC</td>
<td>打开此开关后，使用 ParNew + Serial Old 的收集器组合进行内存回收</td>
</tr>
<tr>
<td>UseConcMarkSweepGC</td>
<td>打开此开关后，使用 ParNew + CMS + Serial Old 的收集器组合进行内存回收。Serial Old 收集器将作为 CMS 收集器出现 Concurrent Mode Failure 失败后的后备收集器使用</td>
</tr>
<tr>
<td><strong>UseParallelGC</strong></td>
<td><strong>虚拟机运行在 Server 模式下的默认值</strong>，打开此开关后，使用 <strong>Parallel Scavenge + Serial Old(PS MarkSweep)</strong> 的收集器组合进行内存回收</td>
</tr>
<tr>
<td>UseParallelOldGC</td>
<td>打开此开关后，使用 Parallel Scavenge + Parallel Old 的收集器组合进行内存回收</td>
</tr>
<tr>
<td>SurvivorRatio</td>
<td>新生代中 Eden 区域与 Survivor 区域的容量比值，默认为 8，代表 Eden : Survivor &#x3D; 8 : 1</td>
</tr>
<tr>
<td>PretenureSizeThreshold</td>
<td>直接晋升到老年代的对象大小，设置这个参数后，大于这个参数的对象将直接在老年代分配</td>
</tr>
<tr>
<td>MaxTenuringThreshold</td>
<td>晋升到老年代的对象年龄，每个对象在坚持过一次 Minor GC 之后，年龄就增加 1，当超过这个参数值时就进入老年代</td>
</tr>
<tr>
<td>UseAdaptiveSizePolicy</td>
<td>动态调整 Java 堆中各个区域的大小以及进入老年代的年龄</td>
</tr>
<tr>
<td>HandlePromotionFailure</td>
<td>是否允许分配担保失败，即老年代的剩余空间不足以应付新生代的整个 Eden 和 Survivor 区的所有对象都存活的极端情况</td>
</tr>
<tr>
<td>ParallelGCThreads</td>
<td>设置并行 GC 时进行内存回收的线程数</td>
</tr>
<tr>
<td>GCTimeRatio GC</td>
<td>时间占总时间的比率，默认值为 99，即允许 1% 的 GC 时间，仅在使用 Parallel Scavenge 收集器生效</td>
</tr>
<tr>
<td>MaxGCPauseMillis</td>
<td>设置 GC 的最大停顿时间，仅在使用 Parallel Scavenge 收集器时生效</td>
</tr>
<tr>
<td>CMSInitiatingOccupancyFraction</td>
<td>设置 CMS 收集器在老年代空间被使用多少后触发垃圾收集，默认值为 68%，仅在使用 CMS 收集器时生效</td>
</tr>
<tr>
<td>UseCMSCompactAtFullCollection</td>
<td>设置 CMS 收集器在完成垃圾收集后是否要进行一次内存碎片整理，仅在使用 CMS 收集器时生效</td>
</tr>
<tr>
<td>CMSFullGCsBeforeCompaction</td>
<td>设置 CMS 收集器在进行若干次垃圾收集后再启动一次内存碎片整理，仅在使用 CMS 收集器时生效</td>
</tr>
</tbody></table>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/6844904148404535309">JVM垃圾回收算法和垃圾回收器</a></p>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/6844903639794843656">JVM系列(五) - JVM垃圾回收算法</a></p>
<p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1582661">Full GC 和 Minor GC，傻傻分不清楚</a></p>
</blockquote>
<h1 id="FullGC"><a href="#FullGC" class="headerlink" title="FullGC"></a>FullGC</h1><h2 id="什么时候会触发FullGC"><a href="#什么时候会触发FullGC" class="headerlink" title="什么时候会触发FullGC"></a>什么时候会触发FullGC</h2><h3 id="System-gc-方法的调用"><a href="#System-gc-方法的调用" class="headerlink" title="System.gc()方法的调用"></a>System.gc()方法的调用</h3><p>此方法的调用是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC,从而增加Full GC的频率,也即增加了间歇性停顿的次数。强烈影响系建议能不使用此方法就别使用，让虚拟机自己去管理它的内存，可通过通过<code>-XX:+ DisableExplicitGC</code>来禁止RMI调用System.gc。</p>
<h3 id="Metaspace区内存达到阈值"><a href="#Metaspace区内存达到阈值" class="headerlink" title="Metaspace区内存达到阈值"></a>Metaspace区内存达到阈值</h3><p>从JDK8开始，永久代(PermGen)的概念被废弃掉了，取而代之的是一个称为Metaspace的存储空间。Metaspace使用的是本地内存，而不是堆内存，也就是说在默认情况下Metaspace的大小只与本地内存大小有关。-XX:MetaspaceSize&#x3D;21810376B（约为20.8MB）超过这个值就会引发Full GC，这个值不是固定的，是会随着JVM的运行进行动态调整的，与此相关的参数还有多个，详细情况请参考<a target="_blank" rel="noopener" href="https://blog.csdn.net/bolg_hero/article/details/78189621">这篇文章</a></p>
<h3 id="老年代空间不足"><a href="#老年代空间不足" class="headerlink" title="老年代空间不足"></a>老年代空间不足</h3><blockquote>
<p><strong>Survivor区域对象晋升到老年代有两种情况</strong>：</p>
<p>一种是给每个对象定义一个对象计数器，如果对象在Eden区域出生，并且经过了第一次GC，那么就将他的年龄设置为1，在Survivor区域的对象每熬过一次GC，年龄计数器加一，等到到达默认值15时，就会被移动到老年代中，默认值可以通过-XX:MaxTenuringThreshold来设置。<br>另外一种情况是如果JVM发现Survivor区域中的相同年龄的对象占到所有对象的一半以上时，就会将大于这个年龄的对象移动到老年代，在这批对象在统计后发现可以晋升到老年代，但是发现老年代没有足够的空间来放置这些对象，这就会引起Full GC。</p>
</blockquote>
<p>老年代空间只有在<strong>新生代对象转入</strong>及<strong>创建大对象、大数组</strong>时才会出现不足的现象，</p>
<p>当执行Full GC后空间仍然不足，则抛出如下错误：<code>java.lang.OutOfMemoryError: Java heap space</code><br>为避免以上两种状况引起的Full GC，调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。</p>
<h3 id="堆中分配很大的对象"><a href="#堆中分配很大的对象" class="headerlink" title="堆中分配很大的对象"></a>堆中分配很大的对象</h3><p>这个参数可以通过<code>-XX:PretenureSizeThreshold</code>进行设定，大对象或者长期存活的对象进入老年代，典型的大对象就是很长的字符串或者数组，它们在被创建后会直接进入老年代，虽然可能新生代中的Eden区域可以放置这个对象，在要放置的时候JVM如果发现老年代的空间不足时，会触发GC。</p>
<p>所谓大对象，是指需要大量连续内存空间的java对象，例如很长的数组，<strong>此种对象会直接进入老年代，而老年代虽然有很大的剩余空间，但是无法找到足够大的连续空间来分配给当前对象</strong>，此种情况就会触发JVM进行Full GC。</p>
<p>为了解决这个问题，CMS垃圾收集器提供了一个可配置的参数，即-XX:+UseCMSCompactAtFullCollection开关参数，用于在“享受”完Full GC服务之后额外免费赠送一个碎片整理的过程，<strong>内存整理的过程无法并发的，空间碎片问题没有了，但提顿时间不得不变长了</strong>，JVM设计者们还提供了另外一个参数 -XX:CMSFullGCsBeforeCompaction,这个参数用于设置在执行多少次不压缩的Full GC后,跟着来一次带压缩的。</p>
<h3 id="统计得到的Minor-GC晋升到旧生代的平均大小大于老年代的剩余空间"><a href="#统计得到的Minor-GC晋升到旧生代的平均大小大于老年代的剩余空间" class="headerlink" title="统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间"></a>统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间</h3><p>这是一个较为复杂的触发情况，Hotspot<strong>为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象</strong>，在进行Minor GC时，做了一个判断，<strong>如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间，那么就直接触发Full GC</strong>。</p>
<blockquote>
<p>例如程序第一次触发Minor GC后，有6MB的对象晋升到旧生代，那么当下一次Minor GC发生时，首先检查旧生代的剩余空间是否大于6MB，如果小于6MB，则执行Full GC（相当于，提前进行fullgc，而不是把新生代的对象移动过去之后发现内存不足才fullgc）。</p>
<p>当新生代采用PS GC时，方式稍有不同，PS GC是在Minor GC后也会检查，例如上面的例子中第一次Minor GC后，PS GC会检查此时旧生代的剩余空间是否大于6MB，如小于，则触发对旧生代的回收。<br>除了以上4种状况外，对于使用RMI来进行RPC或管理的Sun JDK应用而言，默认情况下会一小时执行一次Full GC。可通过在启动时通过- java -Dsun.rmi.dgc.client.gcInterval&#x3D;3600000来设置Full GC执行的间隔时间或通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc。</p>
</blockquote>
<h3 id="CMS-GC时出现promotion-failed和concurrent-mode-failure"><a href="#CMS-GC时出现promotion-failed和concurrent-mode-failure" class="headerlink" title="CMS GC时出现promotion failed和concurrent mode failure"></a>CMS GC时出现promotion failed和concurrent mode failure</h3><p>对于采用CMS进行老年代GC的程序而言，尤其要注意GC日志中是否有<strong>promotion failed</strong>和<strong>concurrent mode failure</strong>两种状况，当这两种状况出现时可能会触发Full GC。</p>
<p>promotion failed是在进行Minor GC时，survivor space放不下、对象只能放入老年代，而此时老年代也放不下造成的；concurrent mode failure是在</p>
<p>执行CMS GC的过程中同时有对象要放入老年代，而此时老年代空间不足造成的（有时候“空间不足”是CMS GC时当前的浮动垃圾过多导致暂时性的空间不足触发Full GC）。<br>应对措施为：增大survivor space、老年代空间或调低触发并发GC的比率，但在JDK 5.0+、6.0+的版本中有可能会由于JDK的bug29导致CMS在remark完毕后很久才触发sweeping动作。对于这种状况，可通过设置-XX: CMSMaxAbortablePrecleanTime&#x3D;5（单位为ms）来避免。</p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>可以发现其实堆内存的Full GC一般都是<strong>两个原因</strong>引起的，<strong>要么是老年代内存过小，要么是老年代连续内存过小。</strong>无非是这两点，而<strong>元数据区Metaspace引发的Full GC可能是阈值引起的</strong>。</p>
<h2 id="如何排查"><a href="#如何排查" class="headerlink" title="如何排查"></a>如何排查</h2><p><strong>检测JVM堆的情况</strong></p>
<p>方法1. 可以使用JDK的bin目录下的jvisualvm.exe工具来进行实时监测，这个是图形化界面，最为直观，这是一个强大的工具。<br>方法2. 采用jps找到进行id，然后使用jstat -gc pid来实时进行检测。<br>方法3. 运行程序前设置-XX:+PrintGCDetails，-XX:+PrintGCDateStamps参数打印GC的详细信息进行分析。</p>
<blockquote>
<p>参考博客</p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/Hollake/article/details/90484027">https://blog.csdn.net/Hollake/article/details/90484027</a></p>
</blockquote>

    </div>

    
    
    

    <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/JVM/" rel="tag"># JVM</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/2021/12/12/ConcurrentHashMap/" rel="prev" title="JUC之并发安全的HashMap —— ConcurrentHashMap">
                  <i class="fa fa-chevron-left"></i> JUC之并发安全的HashMap —— ConcurrentHashMap
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/2021/12/12/JVM%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84/" rel="next" title="JVM内存结构">
                  JVM内存结构 <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">SongyangJi</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>
  <div class="sidebar-dimmer"></div>
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>





  





</body>
</html>
